/* MIT License
 *
 * Copyright (c) 2012 Reuben Smith, Miami University
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.droidlike.vsig;


import com.jme3.asset.AssetManager;
import com.jme3.bounding.BoundingBox;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control;
import com.jme3.scene.shape.Box;
import java.io.IOException;


/**
 * Agent.
 * 
 * @author smithre5
 */
class Agent implements Control
{
    public static Agent createAttachedAt(Simulation sim, Vector3f position)
    {
        if (sim == null || position == null) {
            return null;
        }
        
        Vector3f[] leaf = sim.getGraph().getLeafPositions();
        RoadNode goal = sim.getGraph().getNode(leaf[sim.getRandom().nextInt(leaf.length)]);
        Agent agent = new Agent(sim, goal);
        agent.nextNode = sim.getGraph().getNearestTo(position, 0);
        agent.setSpatial(agent.createGeometry(sim.getAssetManager()));
        agent.attachAt(sim.getScene3D(), position.clone().multLocal(1, 0, 1).addLocal(0, 4, 0));
        
        return agent;
    }
    
    
    public void attachAt(Node root, Vector3f position)
    {
        if (root == null) {
            return;
        }
        
        root.attachChild(spatial);
        
        if (position != null) {
            spatial.setLocalTranslation(position);
        }
    }
    
    
    public Control cloneForSpatial(Spatial spatial)
    {
        // STUB
        return null;
    }
    

    public Spatial getSpatial()
    {
        return spatial;
    }
    
    
    public void setSpatial(Spatial spatial)
    {
        if (this.spatial == null || spatial == null) {
            if (this.spatial != null) {
                this.spatial.removeControl(this);
            }
            
            this.spatial = spatial;
            
            if (this.spatial != null) {
                this.spatial.addControl(this);
            }
        }
    }


    public void update(float tpf)
    {
        // smithre5: This is a pretty dumb greedy-first AI.
        
        spatial.lookAt(nextNode.getPosition(), nextNode.getUp());
        
        // Too far? Move to next node.
        if (spatial.getLocalTranslation().distanceSquared(nextNode.getPosition()) > 1.0f) {
            if (!sim.willCollide(this)) {
                Vector3f agentMove = spatial.getLocalRotation().getRotationColumn(2).clone()
                    .normalizeLocal().multLocal(MOVE_SPEED * tpf);   
                spatial.move(agentMove);
                timeStuck = 0;
            }
            else {
                timeStuck += tpf;
                if (timeStuck > STUCK_DELAY) {
                    sim.removeAgent(this);
                }
            }
            return;
        }
        
        // At goal?
        if (nextNode.equals(goalNode)) {
            sim.removeAgent(this);
            return;
        }
        
        // Is there a light?
        TrafficSignal signal = sim.getSignal(nextNode);
        if (signal != null) {
            if (!signal.isGreen()) {
                return;
            }
        }
                
        Vector3f[] nextNodes = sim.getGraph().getEdges(nextNode.getPosition());
        
        // No moves and not goal?
        if (nextNodes == null || nextNodes.length == 1) {
            // smithre5: Should probably just try to find another node...
            sim.removeAgent(this);
            return;
        }
        
        // Get next best node.
        Vector3f bestPos = null;
        float bestDistSq = Float.POSITIVE_INFINITY;
        for (Vector3f node : nextNodes) {
            if ((prevNode != null && node.equals(prevNode.getPosition())) || 
                node.equals(nextNode.getPosition())) {
                continue;
            }
            
            float distSq = node.distanceSquared(goalNode.getPosition());
            if (distSq < bestDistSq) {
                bestDistSq = distSq;
                bestPos = node;
            }
        }
        
        // ... no best move?
        if (bestPos == null) {
            sim.removeAgent(this);
            return;
        }
        
        prevNode = nextNode;
        nextNode = sim.getGraph().getNode(bestPos);
    }


    public void render(RenderManager rm, ViewPort vp)
    {
        // STUB
    }


    public void write(JmeExporter ex) throws IOException
    {
        // STUB
    }


    public void read(JmeImporter im) throws IOException
    {
        // STUB
    }
    
    
    private Agent(Simulation sim, RoadNode goal)
    {
        ++instancedCount;
        
        this.sim = sim;
        prevNode = null;
        nextNode = null;
        goalNode = goal;
    }
    
    
    private Geometry createGeometry(AssetManager assetManager)
    {
        Geometry agentGeom = new Geometry("agent:" + instancedCount);
        
        Material agentMat = new Material(assetManager,
            "Common/MatDefs/Misc/Unshaded.j3md");
        agentMat.setColor("Color", ColorRGBA.randomColor());
        
        Box agentMesh = new Box(3.0f, 2.0f, 8.0f);
        agentMesh.setBound(new BoundingBox());
        agentMesh.updateBound();
        
        agentGeom.setMaterial(agentMat);
        agentGeom.setMesh(agentMesh);
        
        return agentGeom;
    }
 
    
    private static final float MOVE_SPEED = 50.0f;
    private static final float STUCK_DELAY = 15.0f;
    private static long instancedCount = 0;    
    
    private Simulation sim;
    private Spatial spatial;
    private RoadNode prevNode, nextNode, goalNode;
    private float timeStuck;
}